package cn.com.seederland.gkrpc.client;

import cn.com.seederland.gkrpc.Request;
import cn.com.seederland.gkrpc.Response;
import cn.com.seederland.gkrpc.ServiceDescriptor;
import cn.com.seederland.gkrpc.codec.Decoder;
import cn.com.seederland.gkrpc.codec.Encoder;
import cn.com.seederland.gkrpc.common.constants.exception.ExceptionConstants;
import cn.com.seederland.gkrpc.common.exception.HttpTransportException;
import cn.com.seeederland.gkrpc.transport.TransportClient;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 调用远程服务的代理类
 */
public class RemoteInvoke implements InvocationHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(RemoteInvoke.class);

    private Class clazz;

    private Encoder encoder;

    private Decoder decoder;

    private TransportSelector selector;


    public RemoteInvoke(Class clazz, Encoder encoder, Decoder decoder, TransportSelector selector){
        this.clazz = clazz;
        this.encoder = encoder;
        this.decoder = decoder;
        this.selector = selector;

    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Request request = new Request();
        request.setServiceDescriptor(ServiceDescriptor.from(clazz, method));
        request.setParameters(args);

        Response response = InvokeRemote(request);
        if (null == response || !response.getCode().equals(ExceptionConstants.SUCCESS_CODE)) {
            throw new HttpTransportException("远程服务调用失败：" + response.getMessage());
        }
        LOGGER.info("服务器执行结果：{}", response);
        return response.getData();
    }

    /**
     * 远程方法调用
     * @param request 请求
     * @return
     */
    private Response InvokeRemote(Request request) {
        TransportClient client = null;
        Response response = null;
        try {
            client = selector.select();
            byte[] outBytes = encoder.encode(request);
            InputStream receive = client.sendAndWaitResponse(new ByteArrayInputStream(outBytes));
            byte[] inBytes = IOUtils.readFully(receive, receive.available());
            response = decoder.decoder(inBytes, Response.class);
        } catch (IOException e) {
                LOGGER.warn(e.getMessage(), e);
                response = new Response();
                response.setCode(ExceptionConstants.HANDLER_ERROR);
                response.setMessage("远程服务执行失败：" + e.getClass() + " : " + e.getCause());
        } finally {
            if (client != null) {
                selector.release(client);
            }
        }
        return response;
    }
}
